Rainbow Brainfuck


jump to demo

(Hi! Let's write a "Hello World!" program using the colored pointers of Rainbow Brainfuck (RBBF). Text inside (parentheses) is treated as a comment, and as you can see, they can be multiline and nested too.

Anyway, onto the actual program. RBBF is based on Brainfuck, but adds multiple pointers. Each of these pointers has a color of their own. On top of having multiple pointers, RBBF decouples the source pointer and the target pointer for each command. For example: in normal brainfuck a "+" means "load the value at the pointer, increase it by one, then store it at the pointer". In RBBF the character color is the source, the background color the target. So a red plus on a green background would mean "load the value from pointer to by red, increase it by one, then store it in the byte pointed to by green". From now on I'll refer to character color with FG (foreground) and background color with BG (background).

My first successful implementation used ten colors: Red, Orange, Yellow, Green, Blue, Indigo, Violet, Cyan, Magenta, Black (ROYGBIV + CMYK). Funny enough, I wasn't even thinking of the rainbow flag when designing this language - I just needed a bunch of colors and rainbows naturally fit. However, when I noticed the new inclusive pride flag also used ten colors I had to switch to that, obviously. So now we have Red, Orange, Yellow, Green, Blue, Violet, Pink, Cyan, Brown and Black.

So our new acronym is ROYGBVPCWK:

- Red
- Orange
- Yellow
- Green
- Blue
- Violet
- Pink
- Cyan
- Brown (blue, red and orange already took the first three letters)
- Black (similar story)

(You might be wondering "wait, wasn't there also *white* on one of these new flag designs, for allies?" Well, yes, but here's the thing: if the foreground and background colors of a command are the same, the background becomes transparent (because otherwise it would be unreadable) showing the all-encompassing white background. Now, that was already a feature before the switch, and in principle we can pick *any* color as the "neutral" background color of course, but I figured this is too fitting as an accidental comment on normativity to remove now)

All pointers start at index zero, but for our program we want each color to have their own unique cell. We can quickly fix that like so)

>>>>>>>>>

(The first ">" says "load the position of FG (black in this case), move it one to the right, then set BG (red pointer in this case) to that position". Then we repeat that, but we load the red pointer's position, move it one to the right, and set the green pointer's position to that, and so on. At the end each color will point to their own unique cell.

(You might be wondering how you are supposed to write those colors. There is a plaintext notation for this. The letters in the acronym can be used to set the FG and BG. FG is set with a capital letter, and BG with a lowercase letter (mnemonic: things in the background are smaller than in the foreground). The bit of code above would have been written in plaintext notation as:

Kr>Ro>Oy>Yg>Gb>Bv>Vp>Pc>Cw>

Also, the color changes are persistent until changed, so incrementing the red pointer three times can be written as Rr+++, no need for Rr+Rr+Rr+ (although that still works of course))

The reason we want a unique cell for each is because we want a place to store the unicode values of the characters "Hello World!". Conveniently, there are nine unique characters in that string, and we have ten pointers with their own cell. This makes it easier to initiate these values than in regular Brainfuck, as we'll see.

For reference, here are the unique characters and their values, in order of ascending value:

" ", 32
"!", 33
"H", 72
"W", 87
"d", 100
"e", 101
"l", 108
"o", 111
"r", 114

So let's set up these values. First up, space. It's a power of two, which is neat. We can use a loop for that!)

++++++++[++++-]

(We initialize black to eight, then in a small loop decrement it back to zero while adding four to the red cell each loop, for a total of 8×4 = 32. Just before entering the loop we also store seven in violet, which will help us later.

Next up is "!", which is 33. That's easy: we just increase the value in red by one and store it in orange)

+

(The next characters will be a bit trickier. First, we set yellow to  34, green to 35 and brown to 36)

+++

(Then we use a magenta loop. Each iteration we decrement magenta by one, increase yellow by one, and increase green by two. The result is that yellow is set to 70, green to 107, and brown is decremented to zero)

[+++-]

(Increasing yellow by two sets it to 72, or "H")

++

(Now we get to use the earlier stored violet: we set blue to 73, using yellow. Then we use an violet loop to add 2×7, resulting in 87, the character code for "W")

+[++-]

(Using our earlier stored green pointer we can set violet to 108, the character code for "l".)

+

(Using violet we quickly set pink to 108+1, then add two more for a total of 111, the code for "o")

+++

(Using pink we set cyan to 111+1+2 = 114, code for "r")

+++

(Then we decrement green to 101, "e", and use it to set brown to 100, which is the last character we needed: "d")

-------

(With all of our characters initialized we can finally print!)

............

(As a point of comparison, here is "Hello World!" in plain brainfuck)

>++++++++[<+++++++++>-]<.>++++[<+++++++>-]
<+.+++++++..+++.>>++++++[<+++++++>-]<++.
------------.>++++++[<+++++++++>-]<+.
<.+++.------.--------.>>>++++[<++++++++>-]<+.




Try it yourself!


(want proof this runs live? Try adding a few dots for extra exclamation marks, or copy/paste some of the examples below)



( 
🪽
🪽
Be not afraid, there is more!
🪽
🪽
)
(Rainbow Brainfuck has a number of extra extensions. The most significant is that loops jump to matching colors. In the next example we first set blue cell to zero, and orange cell to one. Then we enter the orange loop, but from within the orange loop we jump out of it with the blue loop) [-][-]+[[]] (Does this have a use? I don't know, I haven't tried it. But the language supports it so go nuts) (Another addition is that we make use of being able to combine two cells to define operators that only work with two inputs) &|^! (Bitwise AND, OR, XOR and NOT) ^ (XOR blue with yellow) ^ (set value in green pointer to zero (XOR with self)) ^| (set value in R to value in Y) {} (Left shift, right shift) +‡⹋ (+ increase by one, ‡ addition, ⹋ multiplication) -=≡ (decrease by one, = subtraction, ≡ division) (Here is how we could write "Hello World!" using these extensions:) +>{>{>{>{>{>{>{>> .+=.-..+..= +..-.==.=.+. (In plaintext notation: +r>{Ro>{Oy>{Yg>{Gb>{Bv>{Vp>{Pc>Cw> VaYaW.c+OsBaCc.-YaC..RaC+.Bb.RwsGaW +.Cc.-OaC.RsOsC.YsC.Bw+W. To make things more confusing, these extensions are "assembly like" in their behavior:
  • ⹋ returns high and low products in FG and BG, respectively
  • ≡ returns division and modulo in FG and BG, respectively
  • { } ‡ = use a carry flag (shift-with-carry, add-with-carry, sub-with-carry)
    • carry flag is reset by & | ^ !
    • note that shifting a color with itself is effectively an 8+1 bit rotation, e.g. {} is:
      • rotate orange cell bits left, shifting carry to LSB and MSB to carry
      • rotate blue cell bits right, shifting carry to MSB and LSB to carry
)
(Example: print "0123456789") >> (give black, red and orange pointer their own unique cell) ++++ (black and orange cells are 2, red is 3) {{ (black is 8, orange is 8 + 2 = 10) { (black is 3 * 16 = 48 ("0"), red is 0 due to high product) [.+-] (use orange as iterator for print loop) (In plaintext: Kr>Ro>Kk+o+k+r+k{{oak{RmOo[Kk.+Oo-]) (We will soon have functions as well! Or something resembling them. Any single letter of the (latin) alphabet defines a function the first time it is encountered in the current function scope.) (increase orange cell value by 20) a++++#aaaa (Nested function definition and call, increase orange cell value by 100) aa++++#aaaa#aaaa (In plaintext notation, functions are indicated by an @ followed by the letter. So the first example would have been written as @aOo++++#@a@a@a@a) (Currently the VM does not yet implement the functions (although the compiler can already emit the bytecode for it)) (As the examples on the previous page show, functions are lexically defined and scoped, but their definitions also run when encountered) ("Running" is not the same as "calling" here - it does not allocate a call frame) ("define and run a, call it four more times, increasing black cell value by twenty") a++++#aaaa (Defines a (due to lexical scoping), but whether the definition is encountered depends on the initial black cell value at runtime. The snippet increases the black cell by 16 if zero, and by 20 if non-zero) [-][a++++#[]]aaaa (To call a function of an "outer" scope, use uppercase. Here b calls a from within its scope) a(…)# b(…)A# (Another planned extension is allowing functions to be stored in a color, e.g. store a in yellow function slot) (Stored functions can be called with * (function slots are initiated as noops)) * (Trying to store undefined functions defines them and runs them in-line as before, e.g. z is defined lexically, run as encountered, then called from red slot four times) ++++#**** (However, storing happens at runtime. Here q is defined but storing is skipped, and the red function slots are noops) [-][+++#]**** (So what happens when trying jump out of a function?) ab[-]c[#]+c## (It unwinds the call stack to the first function whose function body encloses the instruction pointer. In this case, we jump out of c, but land inside the body of b. Also note that when c is called again, it the cell is non-zero, so we don't jump out of it, avoiding an infinite loop) (Here is a more complicated example. Try reading it from left-to-right and see if you can follow the calls and jumps) +++++a-[-#a][a] (Here is an even trickier one) a*+b[-##+*#*] (
🪽
🪽
And that was the whole language (for now)
🪽
🪽
)